using System;
using System.Web;
using System.Web.UI;
using System.IO;
using System.Collections.Generic;

namespace SoftwareMonkeys.Scriptech.Scripts
{
	public class ScriptLauncher
	{
		static public Control Launch(string scriptName, Page page)
		{
			string scriptPath = HttpContext.Current.Request.ApplicationPath.TrimEnd('/') + "/Scripts/" + scriptName;
				// TODO: Check if needed
				//WebUtilities.MapUrlPath("/Scripts/" + scriptName);
			
			string newScriptPath = GetPreparedScriptletPath(scriptName, scriptPath);
			
			return page.LoadControl(newScriptPath);
		}
		
		static public string GetPreparedScriptletPath(string scriptName, string originalPath)
		{
			
			FileInfo info = new FileInfo(HttpContext.Current.Server.MapPath(originalPath));
			
			string timestamp = info.LastWriteTime.ToString();
			
			string path = GetPreparedScriptletPathFromCache(scriptName);
			
			string cacheTimestamp = (string)HttpContext.Current.Application[scriptName + ".LastModified"];
			
			// If the cache wasn't found or the timestamp doesn't match
			if (path == String.Empty || timestamp != cacheTimestamp)
			{
				path = CreateScriptFromScriptlet (originalPath);
				
				SetPreparedScriptletPathCache(scriptName, originalPath, path);
			}
			
			return path;
		}
		
		static public string GetPreparedScriptletPathFromCache(string scriptName)
		{
			if (HttpContext.Current.Application[scriptName + ".PreparedPath"] == null)
				return String.Empty;
			
			return (string)HttpContext.Current.Application[scriptName + ".PreparedPath"];
		}
		
		static public void SetPreparedScriptletPathCache(string scriptName, string originalPath, string preparedPath)
		{
			FileInfo info = new FileInfo(HttpContext.Current.Server.MapPath(originalPath));
			
			HttpContext.Current.Application[scriptName + ".LastModified"] = info.LastWriteTime.ToString();
			HttpContext.Current.Application[scriptName + ".PreparedPath"] = preparedPath;
		}

		static public string CreateScriptFromScriptlet (string scriptletPath)
		{
			string template = LoadScriptletTemplate ();
			string content = LoadFileContent (HttpContext.Current.Server.MapPath (scriptletPath));
			
			string[] usings = ExtractUsings(content);
			content = RemoveUsings(content);
			
			string fullScript = template.Replace ("${Content}", content);
			fullScript = fullScript.Replace("${Imports}", CreateImportsString(usings));
			
			return SaveTempScript (fullScript, scriptletPath);
		}
				
		static public string CreateImportsString(string[] usings)
		{
			string importsString = String.Empty;
			
			foreach (String usingNamespace in usings)	
			{
				importsString += "<%@ Import Namespace=\"" + usingNamespace + "\" %>\r\n";
			}
			
			return importsString;
		}
		
		static public string RemoveUsings(string content)
		{
			List<string> list = new List<string>();
			List<string> lines = new List<string>(content.Split('\n'));

			
			for (int i = 0; i < lines.Count; i++)
			{
				if (lines[i].Trim().Length > 0
				    && lines[i].Trim().Substring(0, 1) == "#")
				{
					// Remove the using line from the content
					lines.RemoveAt(i);
					i--;
				}
			}
			
			string newContent = String.Empty;
			
			foreach (string line in lines)
				newContent = newContent + "\r\n" + line;
			
			content = newContent;
			
			return content;
		}
		
		static public string[] ExtractUsings(string content)
		{
			List<string> list = new List<string>();
			List<string> lines = new List<string>(content.Split(';'));

			
			for (int i = 0; i < lines.Count; i++)
			{
				if (lines[i].Trim().Length > 0
				    && lines[i].Trim().Substring(0, 1) == "#")
				{
					list.Add(lines[i].Trim().Trim('#').Trim()); // Trims are necessary to get rid of newlines and spaces
				}
			}
			
			return list.ToArray();
		}

		static public string LoadScriptletTemplate ()
		{
			string path = HttpContext.Current.Server.MapPath ("~/ScriptTemplates/Default.ascxt");
			
			return LoadFileContent (path);
		}

		static public string LoadFileContent (string path)
		{
			string content = String.Empty;
			using (StreamReader reader = new StreamReader (System.IO.File.OpenRead (path))) {
				content = reader.ReadToEnd ();
			}
			return content;
		}

		static public string SaveTempScript (string scriptContent, string scriptName)
		{
			Guid id = Guid.NewGuid ();
			
			string name = String.Empty;
			name = Path.GetFileName(scriptName).Replace(".", "_");
			name = id.ToString() + Path.DirectorySeparatorChar + name;
			
			string relativePath = "~/Scripts/tmp/" + name + ".ascx";
			string path = HttpContext.Current.Server.MapPath (relativePath);
			
			if (!Directory.Exists (Path.GetDirectoryName (path)))
				Directory.CreateDirectory (Path.GetDirectoryName (path));
			
			using (StreamWriter writer = System.IO.File.CreateText (path)) {
				writer.Write (scriptContent);
			}
			
			return relativePath;
		}
	}
}

